/*! \file
 * \brief Empty.
 * \author Ivo Wingelaar
 * \date 2011
 * \version 0.1.0
 * \copyright GNU Lesser General Public License v3
 */

#include "MythRandom_AlgorithmMersenneTwister_C.hpp"

namespace Myth
{
namespace Random
{

/** constructor **/
/// \param   int32_t seed
///         The seed you want to initialize the generator with.
AlgorithmMersenneTwister19937_C::AlgorithmMersenneTwister19937_C( int32_t seed) :
    mLastInterval(0)
{
    random_init(seed);
}

/** default destructor **/
AlgorithmMersenneTwister19937_C::~AlgorithmMersenneTwister19937_C()
{
    //
}

/** virtual function **/
/** random_init      **/
//! Initializes the generator with a new seed.
/// \return void
/// \param   int32_t seed
///         The seed to initialize the generator with.
void AlgorithmMersenneTwister19937_C::random_init( int32_t seed)
{
    mMt[0]= seed;

    for(mTi = 1; mTi < 624; mTi++)
        mMt[mTi] = (1812433253UL * (mMt[mTi-1] ^ (mMt[mTi-1] >> 30)) + mTi);

    for(uint32_t i = 0; i < 37; i++)
        get_random_bytes();
}

/** virtual function **/
/** get_random_int   **/
//! Returns a random number in the interval [min, max].
/// \return int32_t
///         A number between min and max (inclusive).
/// \param   int32_t min
///         The smallest number allowed.
/// \param   int32_t max
///         The largest number allowed.
int32_t AlgorithmMersenneTwister19937_C::get_random_int( int32_t min,
                                                           int32_t max)
{
    if(max <= min)
    {
        if (max == min) return min;
        else return 0x80000000;
    }

    uint32_t iran, remainder;
    uint32_t interval = uint32_t(max - min + 1);

    if(interval != mLastInterval)
    {
        mRLimit = uint32_t(((uint64_t)1 << 32) / interval) * interval - 1;
        mLastInterval = interval;
    }
    do
    {
        uint64_t longran  = (uint64_t)get_random_bytes() * interval;
        iran = (uint32_t)(longran >> 32);
        remainder = (uint32_t)longran;
    }
    while(remainder > mRLimit);

    return (int32_t)iran + min;
}

/** virtual function **/
/** get_random_bytes **/
//! Return 32 random bits.
/// \return uint32_t
///         32 random bits.
/// \param  void
uint32_t AlgorithmMersenneTwister19937_C::get_random_bytes(void)
{
    // Generate 32 random bits
    uint32_t y;

    if (mTi >= 624)
    {
        // Generate MERS_N words at one time
        const uint32_t LOWER_MASK = (1LU << 31) - 1;       // Lower MERS_R bits
        const uint32_t UPPER_MASK = 0xFFFFFFFF << 31;      // Upper (32 - MERS_R) bits
        static const uint32_t mag01[2] = {0, 0x9908B0DF};

        int kk;
        for (kk=0; kk < 624-397; kk++)
        {
            y = (mMt[kk] & UPPER_MASK) | (mMt[kk+1] & LOWER_MASK);
            mMt[kk] = mMt[kk+397] ^ (y >> 1) ^ mag01[y & 1];
        }

        for (; kk < 624-1; kk++)
        {
            y = (mMt[kk] & UPPER_MASK) | (mMt[kk+1] & LOWER_MASK);
            mMt[kk] = mMt[kk+(397-624)] ^ (y >> 1) ^ mag01[y & 1];
        }

        y = (mMt[624-1] & UPPER_MASK) | (mMt[0] & LOWER_MASK);
        mMt[624-1] = mMt[397-1] ^ (y >> 1) ^ mag01[y & 1];
        mTi = 0;
    }
    y = mMt[mTi++];

    // Tempering (May be omitted):
    y ^=  y >> 11;
    y ^= (y << 7) & 0x9D2C5680;
    y ^= (y << 15) & 0xEFC60000;
    y ^=  y >> 18;

    return y;
}

/** constructor **/
/// \param   int32_t seed
///         The seed you want to initialize the generator with.
AlgorithmMersenneTwister11213A_C::AlgorithmMersenneTwister11213A_C( int32_t seed) :
    mLastInterval(0)
{
    random_init(seed);
}

/** default destructor **/
AlgorithmMersenneTwister11213A_C::~AlgorithmMersenneTwister11213A_C()
{
    //
}

/** virtual function **/
/** random_init      **/
//! Initializes the generator with a new seed.
/// \return void
/// \param   int32_t seed
///         The seed to initialize the generator with.
void AlgorithmMersenneTwister11213A_C::random_init( int32_t seed)
{
    mMt[0]= seed;

    for(mTi = 1; mTi < 351; mTi++)
        mMt[mTi] = (1812433253UL * (mMt[mTi-1] ^ (mMt[mTi-1] >> 30)) + mTi);

    for(uint32_t i = 0; i < 37; i++)
        get_random_bytes();
}

/** virtual function **/
/** get_random_int   **/
//! Returns a random number in the interval [min, max].
/// \return int32_t
///         A number between min and max (inclusive).
/// \param   int32_t min
///         The smallest number allowed.
/// \param   int32_t max
///         The largest number allowed.
int32_t AlgorithmMersenneTwister11213A_C::get_random_int( int32_t min,
                                                            int32_t max)
{
    if(max <= min)
    {
        if (max == min) return min;
        else return 0x80000000;
    }

    uint32_t iran, remainder;
    uint32_t interval = uint32_t(max - min + 1);

    if(interval != mLastInterval)
    {
        mRLimit = uint32_t(((uint64_t)1 << 32) / interval) * interval - 1;
        mLastInterval = interval;
    }
    do
    {
        uint64_t longran  = (uint64_t)get_random_bytes() * interval;
        iran = (uint32_t)(longran >> 32);
        remainder = (uint32_t)longran;
    }
    while(remainder > mRLimit);

    return (int32_t)iran + min;
}

/** virtual function **/
/** get_random_bytes **/
//! Return 32 random bits.
/// \return uint32_t
///         32 random bits.
/// \param  void
uint32_t AlgorithmMersenneTwister11213A_C::get_random_bytes(void)
{
    // Generate 32 random bits
    uint32_t y;

    if (mTi >= 351)
    {
        // Generate MERS_N words at one time
        const uint32_t LOWER_MASK = (1LU << 19) - 1;       // Lower MERS_R bits
        const uint32_t UPPER_MASK = 0xFFFFFFFF << 19;      // Upper (32 - MERS_R) bits
        static const uint32_t mag01[2] = {0, 0xE4BD75F5};

        int kk;
        for (kk=0; kk < 351-175; kk++)
        {
            y = (mMt[kk] & UPPER_MASK) | (mMt[kk+1] & LOWER_MASK);
            mMt[kk] = mMt[kk+175] ^ (y >> 1) ^ mag01[y & 1];
        }

        for (; kk < 351-1; kk++)
        {
            y = (mMt[kk] & UPPER_MASK) | (mMt[kk+1] & LOWER_MASK);
            mMt[kk] = mMt[kk+(175-351)] ^ (y >> 1) ^ mag01[y & 1];
        }

        y = (mMt[351-1] & UPPER_MASK) | (mMt[0] & LOWER_MASK);
        mMt[351-1] = mMt[175-1] ^ (y >> 1) ^ mag01[y & 1];
        mTi = 0;
    }
    y = mMt[mTi++];

    // Tempering (May be omitted):
    y ^=  y >> 11;
    y ^= (y << 7) & 0x655E5280;
    y ^= (y << 15) & 0xFFD58000;
    y ^=  y >> 17;

    return y;
}

} // namespace Random
} // namespace Myth
